Skip to content

feat: integrate IDfy CRC API for individual court record verification#1316

Merged
RitikaHanish merged 1 commit into
mainfrom
feat/integrate-crc-idfy
Jun 9, 2026
Merged

feat: integrate IDfy CRC API for individual court record verification#1316
RitikaHanish merged 1 commit into
mainfrom
feat/integrate-crc-idfy

Conversation

@bytedex

@bytedex bytedex commented May 29, 2026

Copy link
Copy Markdown
Contributor

Type of Change

  • Bugfix
  • New feature
  • Enhancement
  • Refactoring
  • Dependency updates

Description

Additional Changes

  • This PR modifies the database schema (database migration added)
  • This PR modifies dhall configs/environment variables

Motivation and Context

How did you test it?

Checklist

  • I formatted the code and addressed linter errors ./dev/format-all-files.sh
  • I reviewed submitted code
  • I added unit tests for my changes where possible
  • I added a CHANGELOG entry if applicable

Summary by CodeRabbit

  • New Features
    • Added CRC (court-record) verification: submit requests with entity type (individual/business), name, optional father name, optional DOB, address, PAN, and driver ID.
    • Async submissions return a request ID for tracking; results delivered via task responses tagged with the requestor.
    • Result payloads include case details link, report download link, number of cases/monitoring info, risk summary/type, and overall status.

@coderabbitai

coderabbitai Bot commented May 29, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8caa8d96-154f-4bf9-8be9-48fc3af7d158

📥 Commits

Reviewing files that changed from the base of the PR and between 5ee4505 and f7ec446.

📒 Files selected for processing (6)
  • lib/mobility-core/src/Kernel/External/Verification/Idfy/Client.hs
  • lib/mobility-core/src/Kernel/External/Verification/Idfy/Types/Request.hs
  • lib/mobility-core/src/Kernel/External/Verification/Idfy/Types/Response.hs
  • lib/mobility-core/src/Kernel/External/Verification/Interface/Idfy.hs
  • lib/mobility-core/src/Kernel/External/Verification/Interface/Types.hs
  • lib/mobility-core/src/Kernel/External/Verification/Types.hs
✅ Files skipped from review due to trivial changes (1)
  • lib/mobility-core/src/Kernel/External/Verification/Interface/Idfy.hs
🚧 Files skipped from review as they are similar to previous changes (4)
  • lib/mobility-core/src/Kernel/External/Verification/Idfy/Types/Request.hs
  • lib/mobility-core/src/Kernel/External/Verification/Idfy/Types/Response.hs
  • lib/mobility-core/src/Kernel/External/Verification/Interface/Types.hs
  • lib/mobility-core/src/Kernel/External/Verification/Types.hs

Walkthrough

Adds CRC (Court Record Check) verification end-to-end: request/response types, Idfy request payloads, a Servant client endpoint for ind_court_record, and interface functions to invoke the client and map CRC outputs.

Changes

CRC Verification Feature

Layer / File(s) Summary
Response contract and types
Kernel/External/Verification/Types.hs, Kernel/External/Verification/Idfy/Types/Response.hs
Defines CRCVerificationResponse and CRCEntityType, adds CRCResult to IdfyResult, and updates VerificationResponse JSON handling for ind_court_record.
Idfy request payload types
Kernel/External/Verification/Idfy/Types/Request.hs
Adds CRCVerificationRequest = IdfyRequest CRCVerificationData and payload records CRCVerificationData / CRCEntityDetails (uses Day for dob).
Idfy client API
Kernel/External/Verification/Idfy/Client.hs
Adds VerifyCRCAPI Servant type for v3/tasks/async/verify_with_source/ind_court_record and implements verifyCRCAsync calling callIdfyAPI.
Interface request and response types
Kernel/External/Verification/Interface/Types.hs
Adds VerifyCRCReq, VerifyCRCAsyncResp alias, and extends GetTaskResp with CRCResp VT.CRCVerificationResponse.
Interface implementation and orchestration
Kernel/External/Verification/Interface/Idfy.hs
Exports verifyCRCAsync that builds the CRC payload, calls the Idfy client, returns async request info; extends getTask to handle CRCResult.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

I hopped to the repo, nose all a-quiver,
Court records summoned by bytes I deliver,
Types stitched in branches, a client that sings,
Async requests dancing on digital springs,
Hooray — CRC checks, from rabbit to river! 🐰✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: integrate IDfy CRC API for individual court record verification' accurately and specifically summarizes the main change: adding CRC (Court Record Check) verification support via the IDfy API.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/integrate-crc-idfy

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
lib/mobility-core/src/Kernel/External/Verification/Interface/Idfy.hs (1)

213-242: ⚡ Quick win

Confirm entity_type value expected by IDfy CRC API
lib/mobility-core/src/Kernel/External/Verification/Interface/Idfy.hs hardcodes Idfy.CRCVerificationData.entity_type to "individual". Publicly indexed IDfy resources don’t expose the ind_court_record endpoint (or an authoritative enum of entity_type values), so it can’t be validated whether "individual" is always correct or whether other values (e.g., organization/company) must be used.

What are the valid values for entity_type in IDfy CRC (ind_court_record) for the individual-vs-organization variants?

If multiple values are supported, model entity_type as a typed/configured value (e.g., an enum/ADT) instead of a string literal.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/mobility-core/src/Kernel/External/Verification/Interface/Idfy.hs` around
lines 213 - 242, verifyCRCAsync currently hardcodes
Idfy.CRCVerificationData.entity_type = "individual", but the IDfy CRC API may
accept multiple entity types (individual vs organization) so you must confirm
the canonical values with IDfy docs/support and stop using a string literal;
introduce a typed ADT/enum (e.g., EntityType = Individual | Organization) and
expose it via IdfyCfg or the VerifyCRCReq so the caller can select the correct
variant, update buildIdfyRequest and Idfy.CRCVerificationData construction to
map the new ADT to the API string, and adjust types/signatures where
Idfy.CRCVerificationData.entity_type is set to ensure compile-time safety
instead of hardcoded "individual".
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@lib/mobility-core/src/Kernel/External/Verification/Interface/Idfy.hs`:
- Around line 213-242: verifyCRCAsync currently hardcodes
Idfy.CRCVerificationData.entity_type = "individual", but the IDfy CRC API may
accept multiple entity types (individual vs organization) so you must confirm
the canonical values with IDfy docs/support and stop using a string literal;
introduce a typed ADT/enum (e.g., EntityType = Individual | Organization) and
expose it via IdfyCfg or the VerifyCRCReq so the caller can select the correct
variant, update buildIdfyRequest and Idfy.CRCVerificationData construction to
map the new ADT to the API string, and adjust types/signatures where
Idfy.CRCVerificationData.entity_type is set to ensure compile-time safety
instead of hardcoded "individual".

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 85d2c292-368c-4b73-a72f-c6f9dc6c655a

📥 Commits

Reviewing files that changed from the base of the PR and between cbc823a and 9412c23.

📒 Files selected for processing (6)
  • lib/mobility-core/src/Kernel/External/Verification/Idfy/Client.hs
  • lib/mobility-core/src/Kernel/External/Verification/Idfy/Types/Request.hs
  • lib/mobility-core/src/Kernel/External/Verification/Idfy/Types/Response.hs
  • lib/mobility-core/src/Kernel/External/Verification/Interface/Idfy.hs
  • lib/mobility-core/src/Kernel/External/Verification/Interface/Types.hs
  • lib/mobility-core/src/Kernel/External/Verification/Types.hs

@bytedex bytedex left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ClaudeWeb review this pr

@khuzema786 khuzema786 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: IDfy CRC API Integration

The integration is structurally sound and consistent with the existing IDfy verification patterns (RC, DL, PAN, Udyam). No security issues found — API keys are properly decrypted via decrypt, PII fields are passed through correctly. A few things to address before merging.


Warning — GetTaskResp is a breaking change for all downstream consumers

File: lib/mobility-core/src/Kernel/External/Verification/Interface/Types.hs

Adding CRCResp to the GetTaskResp sum type means every pattern match on GetTaskResp without a wildcard catch in nammayatri/nammayatri will now be non-exhaustive. Please confirm all call sites are updated alongside this PR or in an atomic follow-up.

If GetTaskResp values are ever serialised and stored (Redis, job queue, etc.), old payloads without the "CRCResp" tag will fail to deserialise after this ships.


Warning — Untyped status and risk_type fields (confidence 80)

File: lib/mobility-core/src/Kernel/External/Verification/Types.hs

status :: Maybe Text and risk_type :: Maybe Text are the primary fields callers will branch on. IDfy returns a finite set of values for both ("completed" / "in_progress" / "failed" and "low" / "medium" / "high"). As free-form Text, a misspelled or unexpected value from the API silently produces wrong downstream behavior.

Suggested fix — model them as ADTs with a fallback:

data CRCStatus = CRCCompleted | CRCInProgress | CRCFailed | CRCStatusUnknown Text
  deriving (Show, Generic, ToSchema)

data CRCRiskType = CRCLow | CRCMedium | CRCHigh | CRCRiskUnknown Text
  deriving (Show, Generic, ToSchema)

Same concern applies to report_mode :: Maybe Text in the request type — if the IDfy API accepts only a fixed set of modes, this should be an ADT.


Warning — No tests added (confidence 85)

convertCRCOutputToCRCVerification is a pure function with no external dependencies — it's trivially testable. A unit test covering the happy path (all fields populated) and the all-Nothing degenerate case would catch any field-mapping mistakes (e.g., riskSummary vs risk_summary mixups). No other conversion functions in this file have tests either, but this PR is a good place to start.


Potential — dob :: Maybe Text format undocumented (confidence 65)

File: lib/mobility-core/src/Kernel/External/Verification/Idfy/Types/Request.hs

IDfy typically expects dob in DD-MM-YYYY format. An incorrect format will fail silently at the type level and only surface as a 4xx at runtime. At minimum add a comment:

dob :: Maybe Text  -- Expected format: DD-MM-YYYY

A newtype Dob = Dob Text with a smart constructor would be stronger.


Potential — entity_type hardcoded magic string (confidence 65)

File: lib/mobility-core/src/Kernel/External/Verification/Interface/Idfy.hs (~line 228)

entity_type = "individual" is hardcoded. Since ind_court_record is individual-only per the IDfy docs, this is safe today. CodeRabbit already flagged this. If the intent is always individual, consider:

data CRCEntityType = Individual

instance ToJSON CRCEntityType where
  toJSON Individual = "individual"

Even a one-constructor ADT removes the magic string and makes the intent explicit.


Process — PR description checklist is empty

All checklist items are unchecked. Please tick off formatting/lint check, self-review, and tests (or mark N/A), and add a CHANGELOG.md entry for the new capability.

@khuzema786

Copy link
Copy Markdown
Contributor

I'll fetch the PR diff, description, and comments in parallel to do a thorough review.The gh token doesn't have read:org scope. Let me try the GitHub MCP tool instead.Let me fetch the PR details, diff, and comments using the GitHub MCP tools.Now let me get the PR files and look at the actual diff.Let me read the existing source files for deeper context before writing my review.Now I have everything I need. Let me write the review.Now I'll write the review and post it as inline comments plus a summary.The inline line comments failed to resolve — posting as a summary review instead.Review posted. Here's a summary of what was flagged:


Verdict: Comment (not blocking, but a few things need attention)

The integration follows the established IDfy pattern cleanly. No security issues — keys are properly decrypted, PII is handled correctly. Key findings:

Warnings

  1. GetTaskResp breaking change — Adding CRCResp to this sum type makes every downstream case match non-exhaustive. All call sites in nammayatri/nammayatri must be updated. If this type is ever serialised to a store, old payloads will also break on deserialisation.

  2. status and risk_type are untyped Text — These are the fields callers branch on to decide what to do with a result. IDfy returns a fixed enum of values; modelling as ADTs (with an Unknown Text fallback) makes downstream code exhaustive and safe.

  3. No testsconvertCRCOutputToCRCVerification is a pure function. A unit test covering the happy path and the all-Nothing case costs very little and guards against field-mapping regressions.

Potential Issues

  1. dob :: Maybe Text has no format documentation — IDfy typically expects DD-MM-YYYY. A wrong format is a runtime API error with no compile-time signal.
  2. entity_type = "individual" magic string — CodeRabbit already flagged this; low risk for an individual-only endpoint, but an ADT makes intent explicit.

Process

  • All PR checklist items are unchecked (formatting, self-review, tests, changelog).

data CRCEntityDetails = CRCEntityDetails
{ name :: Text,
father_name :: Maybe Text,
dob :: Maybe Text,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can be of Day type? Also, these fields seem mandatory

@bytedex bytedex force-pushed the feat/integrate-crc-idfy branch from 9412c23 to 0890c6b Compare June 2, 2026 07:44

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
lib/mobility-core/src/Kernel/External/Verification/Idfy/Types/Request.hs (1)

154-154: ⚡ Quick win

dob date format matches IDfy (YYYY-MM-DD)

  • IDfy expects dob in ISO 8601 YYYY-MM-DD, and the existing formatting ("%F") matches that, so the DD-MM-YYYY mismatch concern doesn’t apply.
  • Still worth tightening the type/documentation for dob :: Maybe Text to avoid free-form Text misuse (e.g., use a typed date/newtype).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/mobility-core/src/Kernel/External/Verification/Idfy/Types/Request.hs` at
line 154, The dob field is currently declared as "dob :: Maybe Text" which
allows free-form strings; change it to a strongly typed date (e.g., newtype
IdfyDOB = IdfyDOB Day or a newtype wrapping Text with validation) and update the
request record to use that type instead of Maybe Text; add parsing/formatting
(FromJSON/ToJSON or To/FromText) enforcing the YYYY-MM-DD format and adjust any
constructors, serializers, and places that construct or pattern-match on the dob
field (search for the dob field name in the request type and its JSON instances
to update).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@lib/mobility-core/src/Kernel/External/Verification/Idfy/Types/Request.hs`:
- Line 154: The dob field is currently declared as "dob :: Maybe Text" which
allows free-form strings; change it to a strongly typed date (e.g., newtype
IdfyDOB = IdfyDOB Day or a newtype wrapping Text with validation) and update the
request record to use that type instead of Maybe Text; add parsing/formatting
(FromJSON/ToJSON or To/FromText) enforcing the YYYY-MM-DD format and adjust any
constructors, serializers, and places that construct or pattern-match on the dob
field (search for the dob field name in the request type and its JSON instances
to update).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 927ec18d-33ba-47bb-84c1-94493452e718

📥 Commits

Reviewing files that changed from the base of the PR and between 9412c23 and 0890c6b.

📒 Files selected for processing (6)
  • lib/mobility-core/src/Kernel/External/Verification/Idfy/Client.hs
  • lib/mobility-core/src/Kernel/External/Verification/Idfy/Types/Request.hs
  • lib/mobility-core/src/Kernel/External/Verification/Idfy/Types/Response.hs
  • lib/mobility-core/src/Kernel/External/Verification/Interface/Idfy.hs
  • lib/mobility-core/src/Kernel/External/Verification/Interface/Types.hs
  • lib/mobility-core/src/Kernel/External/Verification/Types.hs
🚧 Files skipped from review as they are similar to previous changes (5)
  • lib/mobility-core/src/Kernel/External/Verification/Types.hs
  • lib/mobility-core/src/Kernel/External/Verification/Interface/Types.hs
  • lib/mobility-core/src/Kernel/External/Verification/Idfy/Types/Response.hs
  • lib/mobility-core/src/Kernel/External/Verification/Idfy/Client.hs
  • lib/mobility-core/src/Kernel/External/Verification/Interface/Idfy.hs

@bytedex bytedex force-pushed the feat/integrate-crc-idfy branch 3 times, most recently from 694b88c to a33ba7e Compare June 2, 2026 13:04
dob :: Maybe Day,
address :: Maybe Text,
panNumber :: Maybe Text,
monitorCase :: Maybe Bool,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this we don't need, and will not have relevant data, let's remove this.

address :: Maybe Text,
panNumber :: Maybe Text,
monitorCase :: Maybe Bool,
reportMode :: Maybe Text,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this we don't need, and will not have relevant data, let's remove this.


data CRCVerificationOutput = CRCVerificationOutput
{ case_details_link :: Maybe Text,
monitor_case :: Maybe Bool,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need of this.

data CRCVerificationOutput = CRCVerificationOutput
{ case_details_link :: Maybe Text,
monitor_case :: Maybe Bool,
new_cases_count :: Maybe Int,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not required.

dob :: Maybe Day,
address :: Maybe Text,
pan_number :: Maybe Text,
monitor_case :: Maybe Bool,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not required

accountId <- decrypt cfg.accountId
let reqData =
Idfy.CRCVerificationData
{ entity_type = "individual",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be enum , individual and business

addr.pin
]

convertCRCOutputToCRCVerification :: CRCVerificationOutput -> VT.CRCVerificationResponse

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of this, we can convert camelCase to snakeCase and vice versa via fromJSON and toJSON,

@bytedex bytedex force-pushed the feat/integrate-crc-idfy branch from a33ba7e to 5ee4505 Compare June 4, 2026 09:43

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
lib/mobility-core/src/Kernel/External/Verification/Idfy/Types/Response.hs (1)

70-72: ⚡ Quick win

Add focused tests for the new ind_court_record decode/encode path.

Please add unit tests covering (1) happy-path CRC payload decode/encode and (2) missing/Nothing result handling so this branch doesn’t regress silently.

Also applies to: 87-87, 104-104

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/mobility-core/src/Kernel/External/Verification/Idfy/Types/Response.hs`
around lines 70 - 72, The new "ind_court_record" branch in Response.hs (the
parseJSON @(IdfyResponse (SourceOutput VT.CRCVerificationResponse)) path that
maps with mapIdfyResponse CRCResult) lacks unit tests; add focused tests that
(1) decode a valid ind_court_record JSON into IdfyResponse (SourceOutput
VT.CRCVerificationResponse) and round-trip encode-back to assert equality
(happy-path), and (2) decode cases where the result field is missing/null to
ensure the Nothing handling path of CRCResult is exercised and asserted; place
tests next to other Idfy Response specs and reference parseJSON, IdfyResponse,
CRCResult and the ind_court_record case so the branch at lines around the
parseJSON mapping is covered.
lib/mobility-core/src/Kernel/External/Verification/Types.hs (1)

194-201: ⚡ Quick win

Model CRC riskType and status as ADTs with Unknown Text fallback.

Keeping these enum-like fields as Maybe Text weakens exhaustive handling and lets unknown wire values leak silently through business logic.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/mobility-core/src/Kernel/External/Verification/Types.hs` around lines 194
- 201, Change CRCVerificationResponse to use ADTs for the enum-like fields:
replace the riskType :: Maybe Text and status :: Maybe Text with types like
RiskType and VerificationStatus (each defined as concrete constructors for known
values plus an Unknown Text fallback, and keep a Maybe wrapper if the whole
field can be absent). Implement FromJSON and ToJSON (or parsing functions) for
RiskType and VerificationStatus so known strings map to specific constructors
and any unrecognized string maps to Unknown with the original text preserved.
Update any code that constructs or pattern-matches on CRCVerificationResponse to
use the new ADTs (e.g., pattern-match on RiskType/VerificationStatus
constructors instead of raw Text) and add conversion helpers if needed to
minimize call-site changes; ensure tests/parsers that consume these fields are
updated accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lib/mobility-core/src/Kernel/External/Verification/Interface/Types.hs`:
- Around line 289-290: You added a new constructor CRCResp to the public sum
type GetTaskResp which is a breaking change; revert that constructor addition
from GetTaskResp and instead introduce a new versioned type (e.g. GetTaskRespV2)
that duplicates all current constructors plus CRCResp, or create a separate
CRC-specific wrapper type, and update only the new consumers/parsers to use
GetTaskRespV2 while leaving existing code using GetTaskResp untouched; reference
the GetTaskResp and CRCResp symbols when making the change so all pattern
matches and FromJSON/ToJSON instances remain stable for downstream code.

---

Nitpick comments:
In `@lib/mobility-core/src/Kernel/External/Verification/Idfy/Types/Response.hs`:
- Around line 70-72: The new "ind_court_record" branch in Response.hs (the
parseJSON @(IdfyResponse (SourceOutput VT.CRCVerificationResponse)) path that
maps with mapIdfyResponse CRCResult) lacks unit tests; add focused tests that
(1) decode a valid ind_court_record JSON into IdfyResponse (SourceOutput
VT.CRCVerificationResponse) and round-trip encode-back to assert equality
(happy-path), and (2) decode cases where the result field is missing/null to
ensure the Nothing handling path of CRCResult is exercised and asserted; place
tests next to other Idfy Response specs and reference parseJSON, IdfyResponse,
CRCResult and the ind_court_record case so the branch at lines around the
parseJSON mapping is covered.

In `@lib/mobility-core/src/Kernel/External/Verification/Types.hs`:
- Around line 194-201: Change CRCVerificationResponse to use ADTs for the
enum-like fields: replace the riskType :: Maybe Text and status :: Maybe Text
with types like RiskType and VerificationStatus (each defined as concrete
constructors for known values plus an Unknown Text fallback, and keep a Maybe
wrapper if the whole field can be absent). Implement FromJSON and ToJSON (or
parsing functions) for RiskType and VerificationStatus so known strings map to
specific constructors and any unrecognized string maps to Unknown with the
original text preserved. Update any code that constructs or pattern-matches on
CRCVerificationResponse to use the new ADTs (e.g., pattern-match on
RiskType/VerificationStatus constructors instead of raw Text) and add conversion
helpers if needed to minimize call-site changes; ensure tests/parsers that
consume these fields are updated accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6d8d6b04-6b20-450a-8f4c-ece923cf675f

📥 Commits

Reviewing files that changed from the base of the PR and between a33ba7e and 5ee4505.

📒 Files selected for processing (6)
  • lib/mobility-core/src/Kernel/External/Verification/Idfy/Client.hs
  • lib/mobility-core/src/Kernel/External/Verification/Idfy/Types/Request.hs
  • lib/mobility-core/src/Kernel/External/Verification/Idfy/Types/Response.hs
  • lib/mobility-core/src/Kernel/External/Verification/Interface/Idfy.hs
  • lib/mobility-core/src/Kernel/External/Verification/Interface/Types.hs
  • lib/mobility-core/src/Kernel/External/Verification/Types.hs
🚧 Files skipped from review as they are similar to previous changes (1)
  • lib/mobility-core/src/Kernel/External/Verification/Idfy/Client.hs

@RitikaHanish

Copy link
Copy Markdown
Member

@ClaudeWeb Please review this PR while also checking error handling part.

@ClaudeWeb ClaudeWeb left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: Error Handling Deep-Dive (second pass)

I read the full source of Interface/Idfy.hs, Idfy/Types/Response.hs, and Kernel/Utils/JSON.hs. Two issues are blocking; the rest are warnings.


Bug 1 — CRCEntityType JSON round-trip mismatch (confidence 95)

File: lib/mobility-core/src/Kernel/External/Verification/Types.hs

Generic deriving ToJSON/FromJSON on a sum type uses constructor names verbatim → serialises as "Individual" / "Business" (capitalised). But crcEntityTypeToText produces "individual" / "business" (lowercase). If VerifyCRCReq.entityType is ever stored as JSON (DB, job queue, audit log) and then read back, FromJSON will fail to parse the capitalised value. Silent deserialization failure, only surfacing at runtime.

Fix: Add explicit instances using constructorsToLowerOptions (already in Kernel.Utils.JSON):

instance ToJSON CRCEntityType where
  toJSON = A.genericToJSON constructorsToLowerOptions

instance FromJSON CRCEntityType where
  parseJSON = A.genericParseJSON constructorsToLowerOptions

This also makes crcEntityTypeToText redundant.


Bug 2 — Response.hs uses the public interface type directly, breaking layer separation (confidence 90)

File: lib/mobility-core/src/Kernel/External/Verification/Idfy/Types/Response.hs

Every other IDfy verification follows a two-layer pattern:

  • IDfy layer (Response.hs): native snake_case fields matching the wire format (DLVerificationOutput, PanVerificationOutput, etc.)
  • Interface layer (Types.hs): public camelCase type converted in Interface/Idfy.hs

This PR skips the IDfy layer for CRC and instead puts constructorsWithSnakeCase JSON options on VT.CRCVerificationResponse — a public interface type — to match the wire format. Two concrete error-handling consequences:

a) Silent all-Nothing on field name drift. With native snake_case fields, a mismatched field name is a compile error. With constructorsWithSnakeCase, the mapping is implicit. If IDfy returns case_detail_link (no 's') instead of case_details_link, Aeson silently returns Nothing. Since every field is Maybe, the whole response parses successfully as all-Nothing — no error, no log, just empty data.

b) IDfy wire format leaks into the public type. If IDfy renames a field, VT.CRCVerificationResponse must change, breaking all downstream consumers.

Fix: Restore the two-layer pattern — add CRCVerificationOutput with native snake_case fields to Response.hs, add convertCRCOutputToCRCVerification in Interface/Idfy.hs, and remove constructorsWithSnakeCase from VT.CRCVerificationResponse in Types.hs.

Note: I confirmed in Kernel/Utils/JSON.hs that constructorsWithSnakeCase = defaultOptions { fieldLabelModifier = camelToSnakeCase } (via camelTo2 '_'). The field mapping itself is correct (caseDetailsLinkcase_details_link). The issue is architectural.


Warning — getTask throws InternalError on every poll of an in-progress CRC task (confidence 80)

File: lib/mobility-core/src/Kernel/External/Verification/Interface/Idfy.hs

result <- resp.result & fromMaybeM (InternalError ("Missing result in getTask response: " <> show resp))

When IDfy task status is "in_progress", result is null. fromMaybeM throws InternalError on every poll until completion. This is pre-existing for DL/RC/PAN but is more acute for court records: IDfy's ind_court_record can take minutes to hours. Every poll in that window generates a 500/alert.

Minimal fix within this PR scope: check resp.status before fromMaybeM:

when (resp.status == "in_progress") $
  throwError $ InternalError "CRC verification in progress"
result <- resp.result & fromMaybeM (InternalError ...)

Warning — Business entity type has no guard for ind_court_record endpoint (confidence 75)

The ind_ prefix in IDfy's naming means individual-only. Passing entity_type: "business" will produce a 4xx from IDfy, propagating as an unhandled exception. Either remove Business from the ADT if unsupported, or add:

when (req.entityType == VT.Business) $
  throwError $ InternalError "Business entity type not supported for ind_court_record"

Potential — monitor_case removed from request (confidence 70)

IDfy's ind_court_record supports monitor_case :: Bool for ongoing monitoring of new case filings — likely a primary use case for periodic background checks on drivers. This was in the first revision and removed in the current one. If intentional, please add a comment explaining why.

@khuzema786

Copy link
Copy Markdown
Contributor

Let me re-fetch the PR diff and look deeper at error handling patterns in the surrounding codebase.Good, I have the full source. Now let me fetch the Response.hs file and the JSON utilities to deeply analyze error handling.Now I have everything I need. Let me write the detailed error-handling-focused review.GitHub MCP is failing auth. Let me try posting via the gh CLI directly.Review posted. Here's a summary of everything found in this error-handling pass:


Review Summary (Error Handling Focus)

After reading the full source of Interface/Idfy.hs, Idfy/Types/Response.hs, and Kernel/Utils/JSON.hs:

Blocking Bugs

1. CRCEntityType JSON round-trip mismatch (confidence 95)
Generic ToJSON/FromJSON produces "Individual" / "Business" (capitalised), but crcEntityTypeToText produces "individual" / "business" (lowercase). If entityType is ever stored as JSON and read back, FromJSON silently fails at runtime. Fix: use constructorsToLowerOptions for the instances (already available in Kernel.Utils.JSON).

2. Response.hs uses VT.CRCVerificationResponse directly — layer separation violation (confidence 90)
Every other IDfy type has a dedicated IDfy-layer type with native snake_case fields in Response.hs, plus a conversion function. CRC skips this and puts constructorsWithSnakeCase JSON options on the public interface type. The consequences:

  • Silent all-Nothing if IDfy's actual field names drift even slightly (e.g., case_detail_link vs case_details_link) — since all fields are Maybe, parsing "succeeds" with empty data and no error
  • IDfy's wire format leaks into the public API; any IDfy field rename forces a breaking interface change

Fix: add CRCVerificationOutput with native snake_case fields to Response.hs and a convertCRCOutputToCRCVerification function in Interface/Idfy.hs.

Warnings

3. getTask throws InternalError on in-progress tasks (confidence 80, pre-existing but worsened here) — Court record verification can take minutes to hours. Every status poll until completion throws InternalError → 500/alert. A minimal fix is to check resp.status == "in_progress" before fromMaybeM.

4. Business entity type with ind_court_record endpoint (confidence 75) — Will produce a 4xx from IDfy at runtime with no compile-time or pre-call guard.

5. monitor_case removed from request (confidence 70) — A key IDfy feature for ongoing monitoring was dropped silently between revisions; if intentional, needs a comment.

@RitikaHanish RitikaHanish left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please resolve the claude comment

@bytedex bytedex force-pushed the feat/integrate-crc-idfy branch from 5ee4505 to f7ec446 Compare June 8, 2026 11:19
@bytedex

bytedex commented Jun 8, 2026

Copy link
Copy Markdown
Contributor Author

2. Response.hs uses VT.CRCVerificationResponse directly — layer separation violation (confidence 90) Every other IDfy type has a dedicated IDfy-layer type with native snake_case fields in Response.hs, plus a conversion function. CRC skips this and puts constructorsWithSnakeCase JSON options on the public interface type. The consequences:

  • Silent all-Nothing if IDfy's actual field names drift even slightly (e.g., case_detail_link vs case_details_link) — since all fields are Maybe, parsing "succeeds" with empty data and no error
  • IDfy's wire format leaks into the public API; any IDfy field rename forces a breaking interface change

Fix: add CRCVerificationOutput with native snake_case fields to Response.hs and a convertCRCOutputToCRCVerification function in Interface/Idfy.hs.

@ClaudeWeb this is expected as the flow here is using constructorsWithSnakeCase in parseJSON directly for CRCVerificationResponse and not a separate parsing function would be required. Also fixed the 1st bug, re-review

@ClaudeWeb ClaudeWeb left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review after latest commit

Bug 1 (CRCEntityType JSON mismatch) — Fixed ✓

CRCEntityType now has explicit ToJSON/FromJSON/ToSchema instances using constructorsToLowerOptions. The round-trip is now consistent: both serialization and the wire format use lowercase "individual" / "business". Good fix.

Minor follow-up: crcEntityTypeToText is now redundant — constructorsToLowerOptions already produces the same lowercase strings. Not blocking, but the function can be removed and callers can use toJSON or a direct pattern match instead. Keeping it isn't wrong, just dead weight.


Bug 2 (layer separation) — Accepting the design, one residual concern remains

Understood — using constructorsWithSnakeCase directly in parseJSON is an intentional design choice to avoid a separate IDfy-layer type. The field mapping itself is technically correct: camelTo2 '_' correctly converts caseDetailsLinkcase_details_link, numberOfCasesnumber_of_cases, etc.

I'm withdrawing the architectural objection.

What does remain is a parsing robustness concern that exists regardless of approach: every field in CRCVerificationResponse is Maybe, so a field name mismatch between this type and IDfy's actual response will silently produce Nothing with no parse error. This is inherent to the all-Maybe design, not to the single-type approach specifically. If you discover down the road that IDfy returns case_detail_link (no 's') or risk_level instead of risk_type, you will get an all-Nothing response that looks like a successful parse. A comment documenting the exact IDfy field names (with a link to the API spec) would help whoever debugs this first.


Remaining open items

Warning — getTask throws InternalError for in-progress CRC tasks

result <- resp.result & fromMaybeM (InternalError ("Missing result in getTask response: " <> show resp))

When status = "in_progress" and result = null, this throws InternalError on every poll. Court record verification is a slow check (minutes to hours per IDfy docs). Unless callers explicitly avoid polling until status is "completed", this will generate spurious 500s/alerts throughout the wait. Consider at minimum checking resp.status before fromMaybeM to throw a non-alerting pending signal instead.

Warning — Business entity type with ind_court_record has no guard

Passing entityType = Business will produce a 4xx from IDfy at runtime. Please either remove Business from the ADT or add an explicit check before the API call.

Potential — monitor_case removed from request

If case monitoring (ongoing alerts for new case filings) is needed for this feature, this field should be restored.

@khuzema786

Copy link
Copy Markdown
Contributor

Let me fetch the latest state of the PR to review the Bug 1 fix and re-examine the architecture concern.I have the latest state. Let me write a focused re-review.Review posted. Here's the summary of this pass:


Bug 1 (CRCEntityType JSON mismatch) — Fixed ✓
The explicit ToJSON/FromJSON/ToSchema instances using constructorsToLowerOptions are correct. Minor follow-up: crcEntityTypeToText is now redundant since constructorsToLowerOptions already produces the same lowercase strings — can be cleaned up but not blocking.

Bug 2 (layer separation) — Withdrawn as a bug
Accepting the design decision. constructorsWithSnakeCase via camelTo2 '_' produces the correct field name mapping. The one residual concern — silent all-Nothing on any field name drift from IDfy — is inherent to the all-Maybe design and not specific to this approach. Suggested adding a comment with the exact IDfy field names or an API spec link to help future debugging.

Still open:

  1. getTask throws InternalError on in-progress tasks — Court record checks can take hours; every poll until completion generates a spurious 500/alert
  2. Business entity type with ind_court_record has no guard — Will produce a 4xx from IDfy at runtime with no pre-call validation
  3. monitor_case removed — If ongoing case monitoring is a use case, this field needs to be restored

@RitikaHanish RitikaHanish merged commit b63a48c into main Jun 9, 2026
2 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request Jun 12, 2026
10 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants